package com.csx.orm.mybatis.diy.provider;

import com.csx.orm.mybatis.diy.annotation.Column;
import com.csx.orm.mybatis.diy.annotation.Id;
import com.csx.orm.mybatis.diy.annotation.Table;
import com.csx.orm.mybatis.diy.annotation.Transient;
import com.csx.orm.mybatis.diy.util.ClassUtils;
import com.csx.orm.mybatis.diy.util.StringConvertUtils;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.jdbc.SQL;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * <p> what does this class do ? </p>
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2023/11/30 周四 14:11
 */
public class BaseProvider {

    public String insert(Object params, ProviderContext providerContext) {

        // 获取mapper接口类
        Class<?> mapperClass = providerContext.getMapperType();
        // 获取Mapper接口类上的第一个泛型类
        Class<?> entityClass = ClassUtils.getEntityClass(mapperClass);
        Table table = entityClass.getAnnotation(Table.class);
        // 如果没有定义,则默认驼峰转下划线
        String tableName = table != null ? table.value() : resolveDefaultTableName(entityClass);

        List<String> fields = new ArrayList<>();
        List<String> columns = new ArrayList<>();

        // 解析哪些属性需要插入
        ReflectionUtils.doWithFields(entityClass, field -> {

            String column = field.isAnnotationPresent(Column.class) ? field.getAnnotation(Column.class).value() : StringConvertUtils.camelCaseToUnderscore(field.getName());
            // 设置id
            if(field.isAnnotationPresent(Id.class)) {
                Id idAnno = field.getAnnotation(Id.class);
                Class<? extends Id.IdGenerator> generateClass = idAnno.generateStrategy();
                Id.IdGenerator idGenerator = BeanUtils.instantiateClass(generateClass);
                Object id = idGenerator.next();
                field.setAccessible(true);
                field.set(params, id);
            }

            fields.add(field.getName());
            columns.add(column);
        }, field -> !field.isAnnotationPresent(Transient.class) && !Modifier.isStatic(field.getModifiers()));

        SQL sql = new SQL();
        // 注意此处,添加value的时候需要用占位符而不是直接使用属性名
        sql.INSERT_INTO(tableName).INTO_COLUMNS(columns.toArray(new String[0]))
                .INTO_VALUES(fields.stream().map(field -> "#{" + field + "}").toArray(String[]::new));
        return  sql.toString();
    }

    public String update(Object params, ProviderContext providerContext) {

        // 获取mapper接口类
        Class<?> mapperClass = providerContext.getMapperType();
        // 获取Mapper接口类上的第一个泛型类
        Class<?> entityClass = ClassUtils.getEntityClass(mapperClass);
        Table table = entityClass.getAnnotation(Table.class);
        // 如果没有定义,则默认驼峰转下划线
        String tableName = table != null ? table.value() : resolveDefaultTableName(entityClass);


        // 此处需要记录id的属性名和列名，用于拼接where条件
        final String[] idField = new String[2];

        SQL sql = new SQL();
        // 注意此处,添加value的时候需要用占位符而不是直接使用属性名
       sql.UPDATE(tableName);

        // 解析哪些属性需要插入
        ReflectionUtils.doWithFields(entityClass, field -> {

            String column = field.isAnnotationPresent(Column.class) ? field.getAnnotation(Column.class).value() : StringConvertUtils.camelCaseToUnderscore(field.getName());
            // 设置id
            if(field.isAnnotationPresent(Id.class)) {
                idField[0] = column;
                idField[1] = field.getName();
            }else {
                field.setAccessible(true);
                if(field.get(params) != null) {
                    sql.SET(column + "= #{" + field.getName() + "}");
                }
            }
        }, field -> !field.isAnnotationPresent(Transient.class) && !Modifier.isStatic(field.getModifiers()));


        sql.WHERE(idField[0] + " = #{" + idField[1] + "}");
        return  sql.toString();
    }

    public String get(Object params, ProviderContext providerContext) {
        // 获取mapper接口类
        Class<?> mapperClass = providerContext.getMapperType();
        // 获取Mapper接口类上的第一个泛型类
        Class<?> entityClass = ClassUtils.getEntityClass(mapperClass);

        final String[] idField = new String[2];
        Table table = entityClass.getAnnotation(Table.class);
        // 如果没有定义,则默认驼峰转下划线
        String tableName = table != null ? table.value() : resolveDefaultTableName(entityClass);

        String idColumn;
        SQL sql = new SQL();
        sql.SELECT("*").FROM(tableName);
        // 解析哪些属性需要更新
        ReflectionUtils.doWithFields(entityClass, field -> {
            // 列名的处理（约定大于配置）
            String column = field.isAnnotationPresent(Column.class) && StringUtils.hasText(field.getAnnotation(Column.class).value())
                    ? field.getAnnotation(Column.class).value()
                    : StringConvertUtils.camelCaseToUnderscore(field.getName());
            // id属性记录下来，作为update依据
            if (field.isAnnotationPresent(Id.class)) {
                idField[0] = column;
                idField[1] = field.getName();
            }
        }, field -> !field.isAnnotationPresent(Transient.class) && !Modifier.isStatic(field.getModifiers()));

        sql.AND().WHERE(idField[0] + " = #{" + idField[1] + "}");

        return sql.toString();
    }

    public String findAll(Object params, ProviderContext providerContext) {
        // 获取mapper接口类
        Class<?> mapperClass = providerContext.getMapperType();
        // 获取Mapper接口类上的第一个泛型类
        Class<?> entityClass = ClassUtils.getEntityClass(mapperClass);

        Table table = entityClass.getAnnotation(Table.class);
        // 如果没有定义,则默认驼峰转下划线
        String tableName = table != null ? table.value() : resolveDefaultTableName(entityClass);

        SQL sql = new SQL();
        sql.SELECT("*").FROM(tableName);
        return sql.toString();
    }

    private String resolveDefaultTableName(Class<?> entityClass) {
        String className = entityClass.getSimpleName();
        return StringConvertUtils.camelCaseToUnderscore(className);
    }
}
